import { useState, useEffect, useCallback, useRef } from 'react'

export interface AsyncState<T> {
  data: T | null
  loading: boolean
  error: Error | null
}

export interface UseAsyncOptions {
  immediate?: boolean
  onSuccess?: (data: any) => void
  onError?: (error: Error) => void
}

export function useAsync<T>(
  asyncFunction: () => Promise<T>,
  dependencies: any[] = [],
  options: UseAsyncOptions = {}
) {
  const { immediate = true, onSuccess, onError } = options
  const [state, setState] = useState<AsyncState<T>>({
    data: null,
    loading: false,
    error: null
  })

  const isMountedRef = useRef(true)

  useEffect(() => {
    return () => {
      isMountedRef.current = false
    }
  }, [])

  const execute = useCallback(async () => {
    setState(prev => ({ ...prev, loading: true, error: null }))

    try {
      const data = await asyncFunction()
      
      if (isMountedRef.current) {
        setState({ data, loading: false, error: null })
        onSuccess?.(data)
      }
      
      return data
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error))
      
      if (isMountedRef.current) {
        setState({ data: null, loading: false, error: err })
        onError?.(err)
      }
      
      throw err
    }
  }, dependencies)

  useEffect(() => {
    if (immediate) {
      execute()
    }
  }, [execute, immediate])

  const reset = useCallback(() => {
    setState({ data: null, loading: false, error: null })
  }, [])

  return {
    ...state,
    execute,
    reset
  }
}

// Hook for handling API calls with loading states
export function useApiCall<T>(
  apiFunction: (...args: any[]) => Promise<T>,
  options: UseAsyncOptions = {}
) {
  const [state, setState] = useState<AsyncState<T>>({
    data: null,
    loading: false,
    error: null
  })

  const isMountedRef = useRef(true)

  useEffect(() => {
    return () => {
      isMountedRef.current = false
    }
  }, [])

  const execute = useCallback(async (...args: any[]) => {
    setState(prev => ({ ...prev, loading: true, error: null }))

    try {
      const data = await apiFunction(...args)
      
      if (isMountedRef.current) {
        setState({ data, loading: false, error: null })
        options.onSuccess?.(data)
      }
      
      return data
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error))
      
      if (isMountedRef.current) {
        setState(prev => ({ ...prev, loading: false, error: err }))
        options.onError?.(err)
      }
      
      throw err
    }
  }, [apiFunction, options.onSuccess, options.onError])

  const reset = useCallback(() => {
    setState({ data: null, loading: false, error: null })
  }, [])

  return {
    ...state,
    execute,
    reset
  }
}

// Hook for managing multiple loading states
export function useLoadingStates() {
  const [loadingStates, setLoadingStates] = useState<Record<string, boolean>>({})

  const setLoading = useCallback((key: string, loading: boolean) => {
    setLoadingStates(prev => ({
      ...prev,
      [key]: loading
    }))
  }, [])

  const isLoading = useCallback((key: string) => {
    return loadingStates[key] || false
  }, [loadingStates])

  const isAnyLoading = useCallback(() => {
    return Object.values(loadingStates).some(Boolean)
  }, [loadingStates])

  const clearLoading = useCallback((key?: string) => {
    if (key) {
      setLoadingStates(prev => {
        const { [key]: _, ...rest } = prev
        return rest
      })
    } else {
      setLoadingStates({})
    }
  }, [])

  return {
    loadingStates,
    setLoading,
    isLoading,
    isAnyLoading,
    clearLoading
  }
}
